﻿using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.IdentityModel.Tokens;
using OpenIddict.Abstractions;
using OpenIddict.Server.AspNetCore;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using static OpenIddict.Abstractions.OpenIddictConstants;

namespace openiddict.demo.Controllers
{
    [Route("api/auth")]
    [ApiController]
    public class AuthorizationController : ControllerBase
    {
        private readonly ApplicationDbContext context;
        private readonly AuthorizationService authenticationService;
        private readonly IOpenIddictApplicationManager applicationManager;
        private readonly IOpenIddictAuthorizationManager authorizationManager;
        private readonly IOpenIddictScopeManager scopeManager;

        public AuthorizationController(ApplicationDbContext context, AuthorizationService authenticationService,
            IOpenIddictApplicationManager applicationManager,
            IOpenIddictAuthorizationManager authorizationManager,
            IOpenIddictScopeManager scopeManager)
        {
            this.context = context;
            this.authenticationService = authenticationService;
            this.applicationManager = applicationManager;
            this.authorizationManager = authorizationManager;
            this.scopeManager = scopeManager;
        }





        [HttpPost("token/test")]
        public async Task<IActionResult> Authorize()
        {
            var request = HttpContext.GetOpenIddictServerRequest() ??
                throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");

            //从 cookie中 获取 用户主体。
            var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);

            if (!authenticationService.IsUserAuthenticated(result, request))
            {
                // 解析 请求参数 
                var parameters = authenticationService.ParseOAuthParameters(HttpContext, new List<string> { Parameters.Prompt });

                // 构建重定向 url
                var returnUrl = authenticationService.BuildRedirectUrl(HttpContext.Request, parameters);

                return Challenge(new AuthenticationProperties
                {
                    RedirectUri = returnUrl
                }, CookieAuthenticationDefaults.AuthenticationScheme);
            }



            var application = await applicationManager.FindByClientIdAsync(request.ClientId) ??
                              throw new InvalidOperationException("Details concerning the calling client application cannot be found.");


            var userId = result.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
            var authorizations = await authorizationManager.FindAsync(
                subject: userId,
                client: await applicationManager.GetIdAsync(application),
                status: Statuses.Valid,
                type: AuthorizationTypes.Permanent,
                scopes: request.GetScopes()).ToListAsync();


            var consentType = await applicationManager.GetConsentTypeAsync(application);

            switch (consentType)
            {
                case ConsentTypes.Explicit when !authorizations.Any():
                case ConsentTypes.Explicit when request.HasPromptValue(PromptValues.Consent):
                case ConsentTypes.Systematic:

                    break;

                default:
                    return Forbid(new AuthenticationProperties(new Dictionary<string, string?>
                    {
                        [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidClient,
                        [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "Invalid consent type specified for the client application."
                    }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
            }


            if (request.HasPromptValue(PromptValues.Login))
            {
                var prompt = string.Join(" ", request.GetPromptValues().Remove(PromptValues.Login));

                var parameters = authenticationService.ParseOAuthParameters(HttpContext, new List<string> { Parameters.Prompt });

                parameters[Parameters.Prompt] = prompt;

                var returnUrl = authenticationService.BuildRedirectUrl(HttpContext.Request, parameters);

                await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);

                return Challenge(new AuthenticationProperties { RedirectUri = returnUrl }, CookieAuthenticationDefaults.AuthenticationScheme);
            }



            if (request.HasPromptValue(PromptValues.Consent) || (consentType == ConsentTypes.Explicit && !authorizations.Any()))
            {
                var parameters = authenticationService.ParseOAuthParameters(HttpContext, new List<string> { Parameters.Prompt });
                var returnUrl = authenticationService.BuildRedirectUrl(HttpContext.Request, parameters);

                var consentRedirectUrl = $"/Consent?returnUrl={HttpUtility.UrlEncode(returnUrl)}";
                return Redirect(consentRedirectUrl);
            }






            // 创建 声明
            var identity = new ClaimsIdentity(
                authenticationType: TokenValidationParameters.DefaultAuthenticationType,
                nameType: Claims.Name,
                roleType: Claims.Role);

            // Add Claims  
            var user = await context.Users.FindAsync(Guid.Parse(userId));
            if (user == null)
            {
                return Forbid(new AuthenticationProperties(new Dictionary<string, string>
                {
                    [OpenIddictServerAspNetCoreConstants.Properties.Error] = Errors.InvalidGrant,
                    [OpenIddictServerAspNetCoreConstants.Properties.ErrorDescription] = "The user account is no longer available."
                }), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
            }


            identity.SetClaim(Claims.Subject, userId)
                 .SetClaim(Claims.Email, user.Email)
                 .SetClaim(Claims.Name, user.UserName);


            identity.SetScopes(request.GetScopes());
            identity.SetResources(await scopeManager.ListResourcesAsync(identity.GetScopes()).ToListAsync());


            // 创建 永久授权
            if (consentType is ConsentTypes.Explicit or ConsentTypes.Systematic)
            {
                // Find existing or create new authorization
                var authorization = authorizations.LastOrDefault() ?? await authorizationManager.CreateAsync(
                    identity: identity,
                    subject: userId,
                    client: await applicationManager.GetIdAsync(application),
                    type: AuthorizationTypes.Permanent,
                    scopes: identity.GetScopes());

                identity.SetAuthorizationId(await authorizationManager.GetIdAsync(authorization));
            }


            identity.SetDestinations(claim => AuthorizationService.GetDestinations(identity, claim));

            return SignIn(new ClaimsPrincipal(identity), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
        }









        [HttpPost("token")]
        public async Task<IActionResult> Exchange()
        {
            var request = HttpContext.GetOpenIddictServerRequest() ??
                           throw new InvalidOperationException("The OpenID Connect request cannot be retrieved.");

            if (!request.IsPasswordGrantType())
            {
                return BadRequest(new OpenIddictResponse
                {
                    Error = OpenIddictConstants.Errors.UnsupportedGrantType,
                    ErrorDescription = "The specified grant type is not supported."
                });
            }

            var user = await context.Users.FirstOrDefaultAsync(u => u.UserName == request.Username);
            if (user == null)
            {
                return BadRequest(new OpenIddictResponse
                {
                    Error = OpenIddictConstants.Errors.InvalidGrant,
                    ErrorDescription = "The username/password couple is invalid."
                });
            }

            if (!VerifyPassword(user.PasswordHash, request.Password))
            {
                return BadRequest(new OpenIddictResponse
                {
                    Error = OpenIddictConstants.Errors.InvalidGrant,
                    ErrorDescription = "The username/password couple is invalid."
                });
            }



            var result = await HttpContext.AuthenticateAsync(OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);

            var principal = result.Principal;

            var identity = new ClaimsIdentity(
             authenticationType: TokenValidationParameters.DefaultAuthenticationType,
             nameType: Claims.Name,
             roleType: Claims.Role);


            identity.SetClaim("king", "pengw");

            //return SignIn(new OpenIddictResponse
            //{
            //    AccessToken = "your_token_here",
            //    TokenType = OpenIddictConstants.TokenTypes.Bearer,
            //    ExpiresIn = TimeSpan.FromHours(1)

            //}, OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);

            return SignIn(new ClaimsPrincipal(identity), OpenIddictServerAspNetCoreDefaults.AuthenticationScheme);
        }
        private bool VerifyPassword(string hashedPassword, string plainTextPassword)
        {
            using (var sha256 = SHA256.Create())
            {
                var bytes = sha256.ComputeHash(Encoding.UTF8.GetBytes(plainTextPassword));
                return Convert.ToBase64String(bytes) == hashedPassword;
            }
        }

    }
}
